#include "screens.h"
#include <string.h>
#include <dlfcn.h> 
#include <stdio.h>
#include <unistd.h>
#include "mpp_display_util.h"
void screens::init_screens(const uint32_t width ,const  uint32_t height,const  uint32_t vrefresh,
                                        display_dev::sp_crtc    *p_crtc,               //--当前使用的crtc;
                                        drmModeEncoderPtr  *p_current_encoder,      //--当前使用的encoder;
                                        drmModeConnectorPtr  *p_current_connector//--当前连接器
        )
{

    int ret, i, j;
    if(this->dev){
        
            vector<drmModeConnectorPtr> v_conn = this->dev->mpp_dev_get_connectors();
            vector<drmModeEncoderPtr> v_encoder = this->dev->mpp_dev_get_encoders();
            vector<display_dev::sp_crtc> v_crtc = this->dev->mpp_dev_get_crtcs();
            for (i = 0; i < v_conn.size(); i++) {
                drmModeModeInfoPtr modeInfo = NULL;
                drmModeEncoderPtr encoder = NULL;
                display_dev::sp_crtc *crtc = NULL;
                if (v_conn.at(i)->connection != DRM_MODE_CONNECTED)
                    continue;

                if (!v_conn.at(i)->count_modes) {
                    printf("connector has no modes, skipping\n");
                    continue;
                }
                modeInfo = &v_conn.at(i)->modes[0];
                for (j = 0; j < v_conn.at(i)->count_modes; j++) {
                    drmModeModeInfoPtr tmp_m = &v_conn.at(i)->modes[j];
                    printf(" width :%d height:%d vrefresh:%d type:%02X ,%s\n",tmp_m->hdisplay,
                                                                                                                    tmp_m->vdisplay,
                                                                                                                    tmp_m->vrefresh,
                                                                                                                    tmp_m->type,tmp_m->name);
                                                                                                
                    if (   //(tmp_m->type & DRM_MODE_TYPE_PREFERRED) && 
                                    tmp_m->hdisplay == width && 
                                    tmp_m->vdisplay == height && 
                                    tmp_m->vrefresh == vrefresh){
                            printf("找到最适合的显示模式 width :%d height:%d\n",tmp_m->hdisplay,tmp_m->vdisplay);          
                            modeInfo = tmp_m;
                            break;   
                        }   
                }


                if (!v_conn.at(i)->encoder_id) {
                    /*
                    * default drm encoder not attached connector, just
                    * select the first one.
                    */
                    if (v_encoder.size()) {
                        encoder = v_encoder.at(0);
                        v_conn.at(i)->encoder_id = encoder->encoder_id;
                    } else {
                        printf("no encoder attached to the connector\n");
                        continue;
                    }
                }

                for (j = 0; j < v_encoder.size(); j++) {
                    encoder = v_encoder.at(j);
                    if (encoder->encoder_id == v_encoder.at(j)->encoder_id)
                        break;
                }
                if (j ==  v_encoder.size()) {
                    printf("could not find encoder for the connector\n");
                    continue;
                }

                if (!encoder->crtc_id) {
                /*
                    * default drm crtc not attached encoder, just
                    * select the first one.
                    */
                if (v_crtc.size()) {
                    crtc = &v_crtc.at(0);
                    encoder->crtc_id = crtc->crtc->crtc_id;
                } else {
                    printf("no crtc attached to the encoder\n");
                    continue;
                }

                for (j = 0; j < v_crtc.size(); j++) {
                    crtc = &v_crtc.at(j);

                    if (crtc->crtc->crtc_id == encoder->crtc_id)
                        break;
                }
                if (j ==  v_crtc.size()) {
                    printf("could not find crtc for the encoder\n");
                    continue;
                }


                if (crtc->xrgb_scanout || crtc->yuvnv12_scanout) {
                    printf("crtc already in use\n");
                    continue;
                }
                crtc->xrgb_scanout = this->dev->create_sp_bo(modeInfo->hdisplay, 
                                                                                    modeInfo->vdisplay,                                            
                                    24,  mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_XRGB8888) , DRM_FORMAT_XRGB8888, 0);
                if (!crtc->xrgb_scanout) {
                    printf("failed to create new scanout bo\n");
                    continue;
                }

                
                
                crtc->yuvnv12_scanout = this->dev->create_sp_bo(modeInfo->hdisplay, 
                                                                                                    modeInfo->vdisplay,                                              
                                    16, mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_NV12) , DRM_FORMAT_NV12, 0);// 
                if (! crtc->yuvnv12_scanout) {
                    printf("failed to create new scanout bo\n");
                    continue ;
                }

                crtc->yuvnv12_scanout2 = this->dev->create_sp_bo(modeInfo->hdisplay, 
                                                                                                    modeInfo->vdisplay,                                              
                                    16, mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_NV12) , DRM_FORMAT_NV12, 0);// 
                if (! crtc->yuvnv12_scanout2) {
                    printf("failed to create new scanout bo\n");
                    continue ;
                }
                

                this->dev->fill_bo(crtc->xrgb_scanout, 0x00, 0xFF, 0x0, 0x0);

                ret = drmModeSetCrtc(this->dev->fd, crtc->crtc->crtc_id,
                                crtc->xrgb_scanout->fb_id, 0, 0, &v_conn.at(i)->connector_id,
                                1, modeInfo);
                if (ret) {
                    printf("failed to set crtc mode ret=%d\n", ret);
                    continue;
                }




                crtc->crtc = drmModeGetCrtc(dev->fd, crtc->crtc->crtc_id);
                /*
                * Todo:
                * I don't know why crtc mode is empty, just copy PREFERRED mode
                * for it.
                */
                memcpy(&crtc->crtc->mode, modeInfo, sizeof(*modeInfo));

                *p_crtc = *crtc;                    //--把当前的CRTC记录下来
                *p_current_encoder = encoder;
                *p_current_connector = v_conn.at(i);
            }
        }
        

    }
    
}


/**
 * @brief                      这里RK又个很大的坑，ID为71的图层支持NV12的格式，但是 写入会失败
 * 
 * @param crtc 
 * @param format 
 * @return display_dev::sp_plane* 
 */
display_dev::sp_plane * screens::get_sp_plane(const display_dev::sp_crtc *crtc,const uint32_t format)
{
    if(!crtc || !crtc->crtc || !this->dev)
        return NULL;

    vector <display_dev::sp_plane> tmp_plane = this->dev->mpp_dev_get_plane();
    
    printf("crtc ID:%d 一共得到%d个图层\n",crtc->crtc->crtc_id,crtc->num_planes);     
    for(int j = 0 ;j<crtc->num_planes;j++)
    {
        for(int i = 0;i<tmp_plane.size();i++){
            display_dev::sp_plane *p = &tmp_plane.at(i);
            if( (p->plane->plane_id == 71 || p->plane->plane_id ==101)  && (format == DRM_FORMAT_NV12 || format ==DRM_FORMAT_NV21) )    //--跳过ID为71的图层
                continue;
            if (p->in_use)
                continue;
            if (!(p->plane->possible_crtcs & (1 << crtc->pipe)))
                continue;
            for(int f=0;f<p->plane->count_formats;f++)
                if(p->plane->formats[f]== format){
                    p->in_use = 1;
                    printf("crtc ID:%d 得到plane ID:%d\n",crtc->crtc->crtc_id,p->plane->plane_id);   
                    return p;
                }
        }
    }

    
    return NULL;
}
screens::screens(display_buffer *dev,uint32_t width ,uint32_t height,uint32_t vrefresh):
                                                                                                dev(dev),
                                                                                                m_buffer_obj(NULL),
                                                                                                //current_crtc(NULL),
                                                                                                current_encoder(NULL),
                                                                                                current_connector(NULL),
                                                                                                m_rga_ctx(NULL),
                                                                                                img_bo(NULL),
                                                                                                img_rga_fd(-1),
                                                                                                rga_fd(-1)

{
    display_dev::sp_plane *tmp_plane=NULL;
    if(!this->dev){
         return ;   
    }
    init_screens(width,height,vrefresh,&this->current_crtc,&this->current_encoder,&this->current_connector);

    

    if(!current_crtc.crtc || !this->current_encoder || !this->current_connector)
        return ;
    printf("初始化完成,crtc ID:%d,encoder ID:%d  connector ID:%d \n",
                            current_crtc.crtc->crtc_id,this->current_encoder->encoder_id,this->current_connector->connector_id);
    tmp_plane = get_sp_plane(&current_crtc,DRM_FORMAT_NV12);
    if(!tmp_plane){
        printf("failed to get NV12 plane.\n");
        return ;
    }
    this->NV12_plane = *tmp_plane;

    tmp_plane = get_sp_plane(&current_crtc,DRM_FORMAT_NV12);
    if(!tmp_plane){
        printf("failed to get NV12 plane.\n");
        return ;
    }
    this->NV12_plane2 = *tmp_plane;


    tmp_plane = get_sp_plane(&current_crtc,DRM_FORMAT_XRGB8888);
    if(!tmp_plane){
        printf("failed to get XRGB8888 plane.\n");
        return ;
    }    
    this->XRGB_plane = *tmp_plane;


    
    



    this->m_rga_ctx = new rga_context;
    this->m_rga_ctx->rga_handle = NULL;
    this->m_rga_ctx->rga_handle = dlopen("/usr/lib/librga.so", RTLD_LAZY);
    
    if (!m_rga_ctx->rga_handle) {
        printf("dlopen /usr/lib/librga.so failed\n");
        return;
    }
    printf("rga_handle %p\n",this->m_rga_ctx->rga_handle);

    this->m_rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(m_rga_ctx->rga_handle, "c_RkRgaInit");
    if( !this->m_rga_ctx->init_func){
        printf("get c_RkRgaInit error \n");
        return ;
    }
       
    this->m_rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(m_rga_ctx->rga_handle, "c_RkRgaDeInit");
    if( !this->m_rga_ctx->deinit_func){
        printf("get c_RkRgaDeInit error \n");
        return ;
    }
    this->m_rga_ctx->alloc_buffer = (FUNC_GetAllocBuffer)dlsym(m_rga_ctx->rga_handle, "c_RkRgaGetAllocBuffer");
    if( !this->m_rga_ctx->alloc_buffer){
        printf("get c_RkRgaGetAllocBuffer error \n");
        return ;
    }
    this->m_rga_ctx->get_mmap = (FUNC_GetMmap)dlsym(m_rga_ctx->rga_handle, "c_RkRgaGetMmap");
    if( !this->m_rga_ctx->get_mmap){
        printf("get c_RkRgaGetMmap error \n");
        return ;
    }

    this->m_rga_ctx->get_bufferFd = (FUNC_GetBufferFd)dlsym(m_rga_ctx->rga_handle, "c_RkRgaGetBufferFd");
    if( !this->m_rga_ctx->get_bufferFd){
        printf("get c_RkRgaGetBufferFd error \n");
        return ;
    }
    this->m_rga_ctx->unmap = (FUNC_Unmap)dlsym(m_rga_ctx->rga_handle, "c_RkRgaUnmap");
    if( !this->m_rga_ctx->unmap){
        printf("get unmap error \n");
        return ;
    }
    this->m_rga_ctx->free_buffer = (FUNC_Free)dlsym(m_rga_ctx->rga_handle, "c_RkRgaFree");
    if( !this->m_rga_ctx->free_buffer){
        printf("get c_RkRgaFree error \n");
        return ;
    }

    this->m_rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(m_rga_ctx->rga_handle, "c_RkRgaBlit");
    if( !this->m_rga_ctx->blit_func){
        printf("get c_RkRgaBlit error \n");
        return ;
    }
    
    // memset(&this->rga_bo,0x00,sizeof(bo_t));
    // ret = rga_control_buffer_init(&this->rga_bo,&this->rga_fd, 
    //                                     this->m_screens->width, this->m_screens->height,
    //                                     mpp_disp_util::mpp_format_to_bpp(this->m_screens->format) );
    // if(ret){
    //     printf("rga alloc error \n");
    //     return ;
    // }
    // // -- 注意这里需要使用的是 文件描述符的FD  this->rga_fd 不是bo的fd   而拷贝数据时要使用的是this->rga_bo.ptr
    // printf("RGA BUFFER对象的文件描述符 fd:%d   BUFFER 长度:%d OFFSET:%d PICH:%d  文件描述符 fd:%d \n",
    //                                 this->rga_bo.fd,this->rga_bo.size,this->rga_bo.offset,this->rga_bo.pitch,this->rga_fd);


}
void *screens::display_getImgAddr(uint32_t format,uint32_t index)
{
    if(format == DRM_FORMAT_NV12 || format == DRM_FORMAT_NV21){
            if(index == 0)
                return  this->current_crtc.yuvnv12_scanout->map_addr;
            else if(index ==1 )    
                 return  this->current_crtc.yuvnv12_scanout2->map_addr;
    }else{
        return  this->current_crtc.xrgb_scanout->map_addr;
    }
    return NULL;
}
int screens::display_img(uint32_t position_x ,uint32_t position_y,
                                    uint32_t width ,uint32_t height ,
                                    uint32_t format,int fd,uint32_t BufferIndex)
{
    int  ret = 0;
    display_dev::sp_bo *bo =NULL ;
    display_dev::sp_plane plane; 
    
  
    if(format == DRM_FORMAT_NV12 || format == DRM_FORMAT_NV21){
        plane =  this->NV12_plane;
        if(fd < 0){
            if(BufferIndex == 0)
                bo = this->current_crtc.yuvnv12_scanout;
            else if(BufferIndex == 1)   
                bo = this->current_crtc.yuvnv12_scanout2;
        }else{
           
            bo = this->dev->create_sp_bo(this->current_crtc.crtc->mode.hdisplay, 
                                                           this->current_crtc.crtc->mode.vdisplay,                                            
                                                           16, mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_NV12) , DRM_FORMAT_NV12, 
                                                            fd,0);
            if (! bo) {
                printf("failed to create new scanout bo\n");
                return ret;
            }
            printf("crtc hdisplay %d vdisplay %d fd %d \n",this->current_crtc.crtc->mode.hdisplay,this->current_crtc.crtc->mode.vdisplay,fd);
        }
    }else{
        plane =  this->XRGB_plane;
        if(fd < 0){
            bo = this->current_crtc.xrgb_scanout;
        }else{
            bo = this->dev->create_sp_bo(this->current_crtc.crtc->mode.hdisplay, 
                                                           this->current_crtc.crtc->mode.vdisplay,                                            
                                                           16, mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_XRGB8888) , DRM_FORMAT_XRGB8888, 
                                                            fd,0);
            if (! bo) {
                printf("failed to create new scanout bo\n");
                return ret;
            }
        }
    }
    printf("============= bo size %d width %d height %d  bpp :%d =====================\n",
                                            bo->size,
                                            bo->width,
                                            bo->height,
                                            bo->bpp);     
    ret = drmModeSetPlane(this->dev->fd, plane.plane->plane_id,this->current_crtc.crtc->crtc_id,        
                                                    bo->fb_id, 0,
                                                    /***************************************************************************/
                                                    position_x, position_y,                                                                                                      //--修改这个参数实现平移
                                                    width,height,//this->current_crtc.crtc->mode.hdisplay,this->current_crtc.crtc->mode.vdisplay,          //--修改这个参数实现缩放
                                                    0, 0, 
                                                    (bo->width) << 16, (bo->height) << 16);
    if(ret){
        printf("failed to exec drmModeSetPlane ret=%d\n", ret);
    }

    if(fd > 0){
        if(this->img_bo){
            this->dev->free_sp_bo(this->img_bo,false);
            this->img_bo = NULL;
        }
        this->img_bo = bo;
    }
    return ret;
}
int screens::display_img(void *addr,uint32_t length,int fd ,uint32_t format ,
                                uint32_t width ,uint32_t height ,
                                uint32_t position_x ,uint32_t position_y)
{
    int  ret = 0;
    
    if(fd > 0){    //--如果没有文件描述符的话，就直接在用户空间中使用内存拷贝
        display_dev::sp_bo *bo =NULL ;
        display_dev::sp_plane plane; 
        
        if(format == DRM_FORMAT_NV12 || format == DRM_FORMAT_NV21){
            plane =  this->NV12_plane;
            bo = this->current_crtc.yuvnv12_scanout;
        }else{
            plane =  this->XRGB_plane;
            bo = this->current_crtc.xrgb_scanout;
        }
        memcpy(bo->map_addr,addr,length);

         ret = drmModeSetPlane(this->dev->fd, plane.plane->plane_id,this->current_crtc.crtc->crtc_id,        
                                                    bo->fb_id, 0,

                                                    /***************************************************************************/
                                                    position_x, position_y,                                                                                                      //--修改这个参数实现平移
                                                    this->current_crtc.crtc->mode.hdisplay,this->current_crtc.crtc->mode.vdisplay,          //--修改这个参数实现缩放
                                                    0, 0, 
                                                    (bo->width) << 16, (bo->height) << 16);
            if(ret){
                printf("failed to exec drmModeSetPlane ret=%d\n", ret);
            }
        
    }else{

    }
    
    return ret;                               
}
void screens::drow_background(string path,uint32_t format)
{
    FILE *file_path = NULL;
    uint32_t src_length = 0;
    int ret =0;
    if(this->dev && format == DRM_FORMAT_XRGB8888){
        if(this->current_crtc.xrgb_scanout ){
            file_path = fopen(path.c_str(),"r");
            if(file_path){
                uint8_t *tmpaddr = (uint8_t *)this->current_crtc.xrgb_scanout->map_addr;
                 for(int i = 0;i<this->current_crtc.xrgb_scanout->height;i++){    
                        src_length = fread(&tmpaddr[(i * this->current_crtc.xrgb_scanout->width*4)],1,this->current_crtc.xrgb_scanout->width*4,file_path);
                 }
                 fclose(file_path);
            }
            printf("hdisplay %d vdisplay %d plane ID %d\n",
                                        this->current_crtc.crtc->mode.hdisplay,
                                        this->current_crtc.crtc->mode.vdisplay,
                                        this->XRGB_plane.plane->plane_id);

            ret = drmModeSetPlane(this->dev->fd, 
                             this->XRGB_plane.plane->plane_id,
                             this->current_crtc.crtc->crtc_id, 
                                this->current_crtc.xrgb_scanout->fb_id, 0, 0, 0,
                                this->current_crtc.crtc->mode.hdisplay,
                                this->current_crtc.crtc->mode.vdisplay,
                                0, 0, this->current_crtc.xrgb_scanout->width << 16, this->current_crtc.xrgb_scanout->height << 16);
            if(ret){
                printf("failed to exec drmModeSetPlane.\n");
            }
        }
    }else if(this->dev && format == DRM_FORMAT_NV12){
            if(!this->current_crtc.yuvnv12_scanout){
                this->current_crtc.yuvnv12_scanout = this->dev->create_sp_bo(this->current_crtc.crtc->mode.hdisplay, 
                                                                                   this->current_crtc.crtc->mode.vdisplay,                                            
                                    16, mpp_disp_util::mpp_format_to_bpp(DRM_FORMAT_NV12) , DRM_FORMAT_NV12, 0);// 
                if (! this->current_crtc.yuvnv12_scanout) {
                    printf("failed to create new scanout bo\n");
                    return ;
                }
            }
            printf("bo size %d width %d height %d  bpp :%d\n",
                                            this->current_crtc.yuvnv12_scanout->size,
                                            this->current_crtc.yuvnv12_scanout->width,
                                            this->current_crtc.yuvnv12_scanout->height,
                                            this->current_crtc.yuvnv12_scanout->bpp);     
             file_path = fopen(path.c_str(),"r");
             if(file_path){
                 uint8_t *tmpaddr = (uint8_t *)this->current_crtc.yuvnv12_scanout->map_addr;
                 if(tmpaddr){
                     for(int i = 0;i<1080;i++){  
                            src_length += fread(&tmpaddr[this->current_crtc.yuvnv12_scanout->width*i],1,1920,file_path);
                     }
                 }
                  src_length += fread(&tmpaddr[this->current_crtc.yuvnv12_scanout->width*this->current_crtc.yuvnv12_scanout->height],1,
                                                                this->current_crtc.yuvnv12_scanout->width*this->current_crtc.yuvnv12_scanout->height/2,file_path);
            //        
            //       
                   printf("文件 %s 读取长度%d\n",path.c_str(),src_length);
            //      //}
                  fclose(file_path);
             }

            printf("hdisplay %d vdisplay %d plane ID %d fd ID:%d\n",
                                            this->current_crtc.crtc->mode.hdisplay,
                                            this->current_crtc.crtc->mode.vdisplay,
                                            this->NV12_plane.plane->plane_id,
                                            this->current_crtc.yuvnv12_scanout->fb_id);   

                                            /**
                                             * @brief 
                                             * 当 SRC 与 CRTC 的 X/Y 不相等时，则实现了平移的效果；
                                                当 SRC 与 CRTC 的 W/H 不相等时，则实现了缩放的效果；
                                                当 SRC 与 FrameBuffer 的 W/H 不相等时，则实现了裁剪的效果；
                                             * int fd, uint32_t plane_id, uint32_t crtc_id,
                                                uint32_t fb_id, uint32_t flags,
                                                int32_t crtc_x, int32_t crtc_y,
                                                uint32_t crtc_w, uint32_t crtc_h,
                                                uint32_t src_x, uint32_t src_y,
                                                uint32_t src_w, uint32_t src_h
                                             * 
                                             */

                ret = drmModeSetPlane(this->dev->fd, this->NV12_plane.plane->plane_id,this->current_crtc.crtc->crtc_id,        
                                                    this->current_crtc.yuvnv12_scanout->fb_id, 0,

                                                    /***************************************************************************/
                                                    100, 100,                                                                                                                                                       //--修改这个参数实现平移
                                                    this->current_crtc.crtc->mode.hdisplay/2,this->current_crtc.crtc->mode.vdisplay/2,                                //--修改这个参数实现缩放
                                                    0, 0, 
                                                    (this->current_crtc.yuvnv12_scanout->width) << 16, (this->current_crtc.yuvnv12_scanout->height) << 16);
                if(ret)
                    printf("failed to exec drmModeSetPlane ret=%d\n", ret);
                 
    }
}
screens::~screens()
{
    if( this->m_rga_ctx){
        delete  this->m_rga_ctx;
        this->m_rga_ctx =  NULL;
    }
    if (this->current_crtc.xrgb_scanout) {
            this->dev->free_sp_bo(this->current_crtc.xrgb_scanout,true);
    }
    if (this->current_crtc.yuvnv12_scanout) {
            this->dev->free_sp_bo(this->current_crtc.yuvnv12_scanout,true);
    }
    if (this->current_crtc.yuvnv12_scanout2) {
            this->dev->free_sp_bo(this->current_crtc.yuvnv12_scanout2,true);
    }
    if (this->img_bo) {
         this->dev->free_sp_bo(this->img_bo,false);
    }
}

int screens::rga_control_buffer_init(bo_t *bo, int *buf_fd, int width, int height, int bpp)
{
        int ret;
        // mpp_disp_util::mpp_format_to_bpp
        ret = this->m_rga_ctx->init_func();
        if (ret) {
            printf("c_RkRgaInit error : %s\n", strerror(errno));
            return ret;
        }
        ret = this->m_rga_ctx->alloc_buffer(bo, width, height, bpp);
        if (ret) {
            printf("c_RkRgaGetAllocBuffer error : %s\n", strerror(errno));
            return ret;
        }

        ret = this->m_rga_ctx->get_mmap(bo);
        if (ret) {
            printf("c_RkRgaGetMmap error : %s\n", strerror(errno));
            return ret;
        }

        ret =  this->m_rga_ctx->get_bufferFd(bo, buf_fd);
        if (ret) {
            printf("c_RkRgaGetBufferFd error : %s\n", strerror(errno));
            return ret;
        }
        return 0;
}
void screens::rga_control_buffer_deinit(bo_t *bo, int buf_fd)
{
    int ret;

    if (buf_fd >= 0)
        close(buf_fd);
    if(!bo->ptr)
        return ;
    ret = this->m_rga_ctx->unmap(bo);
    if (ret)
        printf("c_RkRgaUnmap error : %s\n", strerror(errno));
    ret =  this->m_rga_ctx->free_buffer (bo);
    if (ret)
        printf("c_RkRgaFree error : %s\n", strerror(errno));
}